home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / archiver / pdtar.zip / BUFFER.C next >
C/C++ Source or Header  |  1988-05-15  |  13KB  |  611 lines

  1. /*
  2.  * Buffer management for public domain tar.
  3.  *
  4.  * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
  5.  * MS-DOS port 2/87 by Eric Roskos.
  6.  * Minix  port 3/88 by Eric Roskos.
  7.  *
  8.  * @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <sys/types.h>            /* For non-Berkeley systems */
  14. #include <fcntl.h>
  15. #include <signal.h>
  16.  
  17. #include "tar.h"
  18. #include "port.h"
  19.  
  20. #define    STDIN    0                /* Standard input  file descriptor */
  21. #define    STDOUT    1                /* Standard output file descriptor */
  22.  
  23. #define    PREAD    0                /* Read  file descriptor from pipe() */
  24. #define    PWRITE    1                /* Write file descriptor from pipe() */
  25.  
  26. extern char    *valloc();
  27.  
  28. /*
  29.  * V7 doesn't have a #define for this.
  30.  */
  31. #ifndef O_RDONLY
  32. #define    O_RDONLY    0
  33. #endif
  34.  
  35. #define    MAGIC_STAT    105            /* Magic status returned by child, if it
  36.                                  * can't exec compress.  We hope compress
  37.                                  * never returns this status! */
  38. /*
  39.  * The record pointed to by save_rec should not be overlaid
  40.  * when reading in a new tape block.  Copy it to record_save_area first, and
  41.  * change the pointer in *save_rec to point to record_save_area.
  42.  * Saved_recno records the record number at the time of the save.
  43.  * This is used by annofile() to print the record number of a file's
  44.  * header record.
  45.  */
  46. static union record **save_rec;
  47. static union record record_save_area;
  48. static int      saved_recno;
  49.  
  50. /*
  51.  * PID of child compress program, if f_compress.
  52.  */
  53. static int      compress_pid;
  54.  
  55. /*
  56.  * Record number of the start of this block of records
  57.  */
  58. static int      baserec;
  59.  
  60. /*
  61.  * Error recovery stuff
  62.  */
  63. static int      r_error_count;
  64.  
  65.  
  66. /*
  67.  * Return the location of the next available input or output record.
  68.  */
  69. union record   *
  70. findrec()
  71. {
  72.     if (ar_record == ar_last)
  73.     {
  74.         flush_archive();
  75.         if (ar_record == ar_last)
  76.             return (union record *) NULL;        /* EOF */
  77.     }
  78.     return ar_record;
  79. }
  80.  
  81.  
  82. /*
  83.  * Indicate that we have used all records up thru the argument.
  84.  * (should the arg have an off-by-1? XXX FIXME)
  85.  */
  86. void
  87. userec(rec)
  88. union record   *rec;
  89. {
  90.     while (rec >= ar_record)
  91.         ar_record++;
  92.  
  93.     /*
  94.      * Do NOT flush the archive here.  If we do, the same argument to
  95.      * userec() could mean the next record (if the input block is exactly one
  96.      * record long), which is not what is intended. 
  97.      */
  98.     if (ar_record > ar_last)
  99.         abort();
  100. }
  101.  
  102.  
  103. /*
  104.  * Return a pointer to the end of the current records buffer.
  105.  * All the space between findrec() and endofrecs() is available
  106.  * for filling with data, or taking data from.
  107.  */
  108. union record   *
  109. endofrecs()
  110. {
  111.     return ar_last;
  112. }
  113.  
  114.  
  115. /*
  116.  * Open an archive file.  The argument specifies whether we are
  117.  * reading or writing.
  118.  *
  119.  * With DOS, we ALWAYS open the archive in binary mode: whether or not
  120.  * to do CRLF translations depends on whether we open the input files
  121.  * as binary or ASCII, but we always write the archive without making
  122.  * any translations from what this program saw when it did the write.
  123.  */
  124. open_archive(read)
  125. int             read;
  126. {
  127.  
  128.     if (ar_file[0] == '-' && ar_file[1] == '\0')
  129.     {
  130.         if (read)
  131.             archive = STDIN;
  132.         else
  133.             archive = STDOUT;
  134.     }
  135.     else
  136.     if (read)
  137.     {
  138. #ifdef MSDOS
  139.         archive = 9999; /* for debugging - invalid fd to cause err */
  140.         
  141.         if (!f_phys) /* don't open if we're doing direct drive I/O */ 
  142. #endif
  143.             archive = open(ar_file, O_RDONLY 
  144. #ifdef MSDOS
  145.             | O_BINARY
  146. #endif
  147.             );
  148.     }
  149.     else
  150.     {
  151. #ifdef MSDOS
  152.         archive = 9999;
  153.         
  154.         if (!f_phys)
  155. #endif
  156. #ifdef V7
  157.             archive = creat(ar_file, 0666);
  158. #else
  159.             archive = open(ar_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
  160.                 0666);
  161. #endif
  162.     }
  163.  
  164.     if (archive < 0)
  165.     {
  166.         perror(ar_file);
  167.         exit(EX_BADARCH);
  168.     }
  169.  
  170.     /* NOSTRICT */
  171.     ar_block = (union record *) valloc((unsigned) blocksize);
  172.     if (!ar_block)
  173.     {
  174.         fprintf(stderr,
  175.             "tar: could not allocate memory for blocking factor %d\n",
  176.             blocking);
  177.         exit(EX_ARGSBAD);
  178.     }
  179.  
  180.     ar_record = ar_block;
  181.     ar_last = ar_block + blocking;
  182.  
  183.     /*
  184.      * Handle compressed archives. 
  185.      *
  186.      * FIXME, currently supported for reading only. FIXME, writing involves
  187.      * forking again for a small process that will reblock the output of
  188.      * compress to the user's specs. 
  189.      */
  190. #ifndef MSDOS
  191.     if (f_compress)
  192.     {
  193.         int             pipes[2];
  194.         int             err;
  195.  
  196.         if (!read)
  197.         {
  198.             fprintf(stderr,
  199.                 "tar: cannot write compressed archives yet.\n");
  200.             exit(EX_ARGSBAD);
  201.         }
  202.  
  203.         /* Create a pipe to get compress's output to us */
  204.         err = pipe(pipes);
  205.         if (err < 0)
  206.         {
  207.             perror("tar: cannot create pipe to compress");
  208.             exit(EX_SYSTEM);
  209.         }
  210.  
  211.         /* Fork compress process */
  212.         compress_pid = fork();
  213.         if (compress_pid < 0)
  214.         {
  215.             perror("tar: cannot fork compress");
  216.             exit(EX_SYSTEM);
  217.         }
  218.  
  219.         /*
  220.          * Child process. 
  221.          *
  222.          * Move input to stdin, write side of pipe to stdout, then exec
  223.          * compress. 
  224.          */
  225.         if (compress_pid == 0)
  226.         {
  227.             (void) close(pipes[PREAD]);    /* We won't use it */
  228.             if (archive != STDIN)
  229.             {
  230.                 (void) close(STDIN);
  231.                 err = dup(archive);
  232.                 if (err != 0)
  233.                 {
  234.                     perror(
  235.                         "tar: cannot dup input to stdin");
  236.                     exit(EX_SYSTEM);
  237.                 }
  238.                 (void) close(archive);
  239.             }
  240.             if (pipes[PWRITE] != STDOUT)
  241.             {
  242.                 (void) close(STDOUT);
  243.                 err = dup(pipes[PWRITE]);
  244.                 if (err != STDOUT)
  245.                 {
  246.                     perror(
  247.                         "tar: cannot dup pipe output");
  248.                     exit(MAGIC_STAT);
  249.                 }
  250.                 (void) close(pipes[PWRITE]);
  251.             }
  252. #ifdef V7
  253.             execl("/usr/bin/compress", "compress", "-d", (char *)0);
  254. #else
  255.             execlp("compress", "compress", "-d", (char *) 0);
  256. #endif
  257.             perror("tar: cannot exec compress");
  258.             exit(MAGIC_STAT);
  259.         }
  260.  
  261.         /*
  262.          * Parent process.  Clean up. FIXME, note that this may leave
  263.          * standard input closed, if the compressed archive was on standard
  264.          * input. 
  265.          */
  266.         (void) close(archive);    /* Close compressed archive */
  267.         (void) close(pipes[PWRITE]);    /* Close write side of pipe */
  268.         archive = pipes[PREAD];    /* Read side is our archive */
  269.  
  270. #ifdef BSD42
  271.         f_reblock++;            /* Pipe will give random # of bytes */
  272. #endif /* BSD42 */
  273.     }
  274. #endif                            /* MSDOS */
  275.  
  276.     ar_reading = read;
  277.     if (read)
  278.     {
  279.         ar_last = ar_block;        /* Set up for 1st block = # 0 */
  280.         flush_archive();
  281.     }
  282. }
  283.  
  284.  
  285. /*
  286.  * Remember a union record * as pointing to something that we
  287.  * need to keep when reading onward in the file.  Only one such
  288.  * thing can be remembered at once, and it only works when reading
  289.  * an archive.
  290.  */
  291. saverec(pointer)
  292. union record  **pointer;
  293. {
  294.  
  295.     save_rec = pointer;
  296.     saved_recno = baserec + ar_record - ar_block;
  297. }
  298.  
  299. /*
  300.  * Perform a write to flush the buffer.
  301.  */
  302. fl_write()
  303. {
  304.     int             err;
  305.     int        nbytes = blocksize;
  306.  
  307. rewrite:
  308. #ifdef MSDOS
  309.     if (f_phys)
  310.         err = physwrite(ar_block->charptr, nbytes);
  311.     else     
  312. #endif
  313.         err = write(archive, ar_block->charptr, nbytes);
  314.     if (err == nbytes)
  315.         return;
  316.     /* multi-volume support on write -- JER */
  317.     if (err < 0)
  318.         perror(ar_file);
  319.     else
  320. #ifdef MSDOS /* DOS version handles volume change in low-level I/O code */
  321.         fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
  322.             ar_file, blocksize - err);
  323. #else            
  324.     {
  325.         sync(); /* have to flush Minix buffer */
  326.         uprintf(ftty,"\ntar: Volume full.  Change volumes and press [Enter]: ");
  327.         while (ugetc(ftty)!='\n') ;
  328.         nbytes -= err;
  329.         lseek(archive, 0L, 0);
  330.         goto rewrite;
  331.     }
  332. #endif
  333.     exit(EX_BADARCH);
  334. }
  335.  
  336.  
  337. /*
  338.  * Handle read errors on the archive.
  339.  *
  340.  * If the read should be retried, readerror() returns to the caller.
  341.  */
  342. void
  343. readerror()
  344. {
  345. #    define    READ_ERROR_MAX    10
  346.  
  347.     read_error_flag++;            /* Tell callers */
  348.  
  349.     annorec(stderr, tar);
  350.     fprintf(stderr, "Read error on ");
  351.     perror(ar_file);
  352.  
  353.     if (baserec == 0)
  354.     {
  355.         /* First block of tape.  Probably stupidity error */
  356.         exit(EX_BADARCH);
  357.     }
  358.  
  359.     /*
  360.      * Read error in mid archive.  We retry up to READ_ERROR_MAX times and
  361.      * then give up on reading the archive.  We set read_error_flag for our
  362.      * callers, so they can cope if they want. 
  363.      */
  364.     if (r_error_count++ > READ_ERROR_MAX)
  365.     {
  366.         annorec(stderr, tar);
  367.         fprintf(stde